package com.xzz.proxyForHand;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: hhz
 * @create: 2022-03-15 11:15
 **/
public class BeanUtil {
    /**
     * 假设这是单例池 模仿Spring,存放已经弄好的对象
     */
    public final Map<String,Object> beanCache = new ConcurrentHashMap<>(8);

    /**
     * 假设这是bd map 存放bean的类信息 这里相对Spring进行简化了，只存储类对象足够了
     */
    public final Map<String,Class> beanClassCache = new ConcurrentHashMap<>(8);
    /**
     * 假设这是bean容器里面存储切面的缓存
     */
    public final List<Class> aspectjClassCache = new ArrayList<>(8);

    /**
     * 拦截规则 存储注解对应的规则链
     */
    public final Map<String,List<ProxyChain>> proxyRule = new ConcurrentHashMap<>();

    /**
     * 假设Spring完成了基础扫描步骤 已经将bd 存放在容器
     */
    public BeanUtil() throws InstantiationException,IllegalAccessException{
        beanClassCache.put("testService",TestServiceImpl.class);
        aspectjClassCache.add(MyAspect.class);
        parseAspectjClass();
    }

    /**
     * 初始化bean
     */
    public void initBean(){
        beanClassCache.forEach((key,value)->{
            //判断是否有需要代理的方法
            try{
                //创建所有的类 转换为bean
                createBean(key,value,hasProxy(value));
            }catch (IllegalAccessException e){
                e.printStackTrace();
            }catch (InstantiationException e){
                e.printStackTrace();
            }
        });
    }
    /**
     * 创建bean
     */
    public void createBean(String beanName,Class classes,boolean isProxy)throws IllegalAccessException,InstantiationException{
        Class<?>[] interfaces = this.getInterfaces(classes);
        Object target = classes.newInstance();
        //需要被代理 模拟JDK动态代理
        if(isProxy && interfaces != null && interfaces.length>0){
            //创建JDK动态代理的对象
            MyJDKDynamicAopProxy myJDKDynamicAopProxy = new MyJDKDynamicAopProxy(this, target);
            //返回代理对象
            target = Proxy.newProxyInstance(BeanUtil.class.getClassLoader(),interfaces,myJDKDynamicAopProxy);
        }
        //存储在单例池
        beanCache.put(beanName,target);
    }

    /**
     * 内部判断是否存在需要拦截方法的对象
     */
    private boolean hasProxy(Class targetClass){
        //获取所有方法
        Method[] declaredMethods = targetClass.getDeclaredMethods();
        for (Method method : Arrays.asList(declaredMethods)) {
            //获取方法上的的注解
            Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
            //当方法存在注解的时候 开始判断方法是否需要被代理
            if(null!= declaredAnnotations && declaredAnnotations.length>0){
                for (Annotation annotation : Arrays.asList(declaredAnnotations)) {
                    //如果接近规则存在这个注解就返回true
                    if(proxyRule.containsKey(annotation.annotationType().getName())){
                        return true;
                    }
                }
            }
        }
        return false;
    }
    /**
     * 解析切面类
     */
    private void parseAspectjClass()throws IllegalAccessException,InstantiationException{
        for (Class aClass : aspectjClassCache) {
            //获取切面的所有方法
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method method : Arrays.asList(declaredMethods)) {
                //寻找携带MyAround注解的切面方法
                MyAround myAroundAnnotation = method.getAnnotation(MyAround.class);
                if(null!=myAroundAnnotation){
                    //拿到切点标志 也就是未来切点的方法
                    Class<? extends Annotation> targetClassAnnotation = myAroundAnnotation.targetClass();
                    //实例化切面
                    Object proxyTarget = aClass.newInstance();
                    //创建调用链实体
                    ProxyChain proxyChain = new ProxyChain(method, proxyTarget);
                    //构建对应规则的调用链
                    String classAnnotationName = targetClassAnnotation.getName();
                    if (proxyRule.containsKey(classAnnotationName)) {
                        proxyRule.get(classAnnotationName).add(proxyChain);
                    }else {
                        List<ProxyChain> proxyChains = new ArrayList<>();
                        proxyChains.add(proxyChain);
                        proxyRule.put(classAnnotationName,proxyChains);
                    }
                }
            }
        }
    }

    /**
     * 获取类的接口
     */
    private Class<?>[] getInterfaces(Class classes){
        return classes.getInterfaces();
    }
    /**
     * 获取bean对象
     */
    public <T> T getBean(String beanName){
        return (T) beanCache.get(beanName);
    }
}
